package com.upb.webserver.org;

import cn.hutool.core.collection.CollectionUtil;
import com.upb.webserver.account.developer.DeveloperService;
import com.upb.webserver.common.enums.PlatformEnum;
import com.upb.webserver.common.enums.RoleTypeEnum;
import com.upb.webserver.common.exception.WebServerException;
import com.upb.webserver.common.pojo.base.BasePageResponse;
import com.upb.webserver.common.pojo.org.ReqAddOrgVO;
import com.upb.webserver.common.pojo.org.ReqUpdateOrgVO;
import com.upb.webserver.common.returncode.ConstantCode;
import com.upb.webserver.common.tools.JsonUtils;
import com.upb.webserver.common.tools.MybatisExampleTools;
import com.upb.webserver.company.CompanyDeveloperManager;
import com.upb.webserver.company.CompanyDeveloperService;
import com.upb.webserver.dao.entity.TbOrg;
import com.upb.webserver.dao.entity.TbOrgExample;
import com.upb.webserver.dao.mapper.TbOrgMapper;
import com.upb.webserver.host.HostManager;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
public class OrgService {
    @Autowired
    private TbOrgMapper orgMapper;
    @Autowired
    private OrgManager orgManager;
    @Autowired
    private DeveloperService developerService;
    @Autowired
    private CompanyDeveloperService companyDeveloperService;
    @Autowired
    private HostManager hostManager;
    @Autowired
    private CompanyDeveloperManager companyDeveloperManager;

    /**
     * add new org.
     */
    public TbOrg addOrg(byte platform, int currentAccountId, ReqAddOrgVO param) throws WebServerException {
        log.info("start exec method [addOrg]. platform:{} currentAccountId:{} param:{}", platform, currentAccountId, JsonUtils.objToString(param));

        int belongToDeveloper = currentAccountId;
        if (PlatformEnum.DEVELOPER.getId() != platform) {
            belongToDeveloper = param.getBelongToDeveloper();
        }

        log.debug("belongToDeveloper:{}", belongToDeveloper);
        developerService.verifyDeveloperId(belongToDeveloper);

        //get companyId
        Integer companyId = companyDeveloperService.selectCompanyIdByDeveloperId(belongToDeveloper);

        //check name
        TbOrgExample example = MybatisExampleTools.initSampleExample(TbOrgExample.class, new TbOrg(companyId, param.getName()));
        if (orgMapper.countByExample(example) > 0) {
            log.warn("fail exec method[addOrg], found org record by companyId:{} and orgName:{}", companyId, param.getName());
            throw new WebServerException(ConstantCode.DUPLICATE_ORG_NAME);
        }

        //save developer
        TbOrg tbOrg = new TbOrg();
        BeanUtils.copyProperties(param, tbOrg);
        tbOrg.setCreateFromPlatform(platform);
        tbOrg.setCreateBy(currentAccountId);
        tbOrg.setDeveloperId(belongToDeveloper);
        tbOrg.setCompanyId(companyId);
        Date createTime = new Date();
        tbOrg.setGmtCreate(createTime);
        tbOrg.setGmtModified(createTime);

        orgMapper.insertSelective(tbOrg);

        TbOrg orgResult = orgMapper.selectByPrimaryKey(tbOrg.getId());
        log.info("success exec method [addOrg]");
        return orgResult;
    }


    /**
     * update by id.
     *
     * @param param
     */
    public void updateOrg(ReqUpdateOrgVO param) {
        log.info("start exec method [updateOrg]. param:{}", JsonUtils.objToString(param));

        //check id
        TbOrg tbOrg = orgManager.verifyOrgId(param.getId());

        //check orgId used
        if (hostManager.countByOrgId(param.getId()) > 0) {
            log.warn("org has bean used by host,orgId:{}", param.getId());
            throw new WebServerException(ConstantCode.ORG_ID_HAS_BEEN_USED);
        }

        //check orgName
        TbOrgExample example = MybatisExampleTools.initSampleExample(TbOrgExample.class, new TbOrg(tbOrg.getCompanyId(), param.getName()));
        Optional<TbOrg> orgRecordByNameOpt = orgMapper.getOneByExample(example);
        if (orgRecordByNameOpt.isPresent() && !orgRecordByNameOpt.get().getId().equals(param.getId())) {
            log.warn("fail exec method [updateOrg] , found record by name:{},and the record id is:{},but input id is:{}", param.getName(), orgRecordByNameOpt.get().getId(), param.getId());
            throw new WebServerException(ConstantCode.DUPLICATE_ORG_NAME);
        }

        //update
        BeanUtils.copyProperties(param, tbOrg);
        orgMapper.updateByPrimaryKeyWithBLOBs(tbOrg);
        log.info("success exec method [updateOrg]");
    }


    /**
     * @param platform
     * @param currentAccountId
     * @param pageNumber
     * @param pageSize
     * @return
     */
    public BasePageResponse queryOrgByPage(byte platform, String currentAccountRole, int currentAccountId, int pageNumber, int pageSize, Integer developerId) {
        log.info("start exec method [queryOrgByPage].currentAccountRole:{} currentAccountId:{} pageNumber:{} pageSize:{} developerId:{}", currentAccountRole, currentAccountId, pageNumber, pageSize, developerId);
        TbOrgExample example = new TbOrgExample();
        example.setStart(Optional.ofNullable(pageNumber).map(page -> (page - 1) * pageSize).orElse(0));
        example.setCount(pageSize);

        int companyId = 0;
        if (platform == PlatformEnum.DEVELOPER.getId()) {
            companyId = companyDeveloperService.selectCompanyIdByDeveloperId(currentAccountId);
        } else if (null != developerId) {
            companyId = companyDeveloperService.selectCompanyIdByDeveloperId(developerId);
        }

        TbOrgExample.Criteria criteria = example.createCriteria();
        if (companyId > 0)
            criteria.andCompanyIdEqualTo(companyId);

        if (RoleTypeEnum.API_MANAGER.getName().equalsIgnoreCase(currentAccountRole)) {
            List<Integer> companyIdList = companyDeveloperManager.listCompanyIdByPlatformAndAccountId(platform, currentAccountId);
            if (CollectionUtil.isEmpty(companyIdList))
                return BasePageResponse.initialize();

            criteria.andCompanyIdIn(companyIdList);
        }


        BasePageResponse pageResponse = orgManager.queryOrgByPage(example);
        log.info("success exec method [queryOrgByPage]. result:{}", JsonUtils.objToString(pageResponse));
        return pageResponse;
    }


    /**
     * @param orgIdList
     * @return
     */
    public List<Integer> listDeveloperIdByOrgIdList(List<Integer> orgIdList) {
        log.info("start exec method [listDeveloperIdByOrgIdList]. orgIdList:{}", JsonUtils.objToString(orgIdList));
        List<TbOrg> orgList = orgMapper.listByOrgIdList(orgIdList);
        if (Objects.isNull(orgList) || orgList.size() == 0) {
            log.warn("unsuccessfully exec method [listDeveloperIdByOrgIdList]. not found record by orgIdList:{}", JsonUtils.objToString(orgIdList));
            return Collections.emptyList();
        }
        List<Integer> companyIdList = orgList.stream().map(o -> o.getCompanyId()).collect(Collectors.toList());
        List<Integer> developerIdList = companyDeveloperService.ListDeveloperIdByCompanyIdList(companyIdList);
        log.info("success exec method [listDeveloperIdByOrgIdList]. developerIdList:{}", JsonUtils.objToString(developerIdList));
        return developerIdList;
    }


    /**
     * @param developerId
     * @return
     */
    public Set<Integer> getOrgIdSetByDeveloperId(int developerId) {
        log.info("start exec method [getOrgIdSetByDeveloperId]. developerId:{}", developerId);
        Integer companyId = companyDeveloperService.selectCompanyIdByDeveloperId(developerId);
        Set<Integer> listOrgId = getOrgIdSetByCompanyId(companyId);
        log.info("success exec method [getOrgIdSetByDeveloperId]. developerId:{} result:{}", developerId, JsonUtils.objToString(listOrgId));
        return listOrgId;
    }

    /**
     * @param companyId
     * @return
     */
    public Set<Integer> getOrgIdSetByCompanyId(int companyId) {
        log.info("start exec method [getOrgIdSetByCompanyId]. companyId:{}", companyId);
        List<TbOrg> orgList = orgManager.listOrgByCompanyId(companyId);
        if (CollectionUtil.isEmpty(orgList)) {
            log.warn("fail exec method[getOrgIdSetByCompanyId]. not found org by companyId:{}", companyId);
            return CollectionUtil.newHashSet();
        }
        Set<Integer> listOrgId = orgList.stream().map(o -> o.getId()).collect(Collectors.toSet());
        log.info("success exec method [getOrgIdSetByCompanyId]. companyId:{} result:{}", companyId, JsonUtils.objToString(listOrgId));
        return listOrgId;
    }


    /**
     * @param orgId
     */
    public void deleteByOrgId(int orgId) {
        log.info("start exec method [deleteByOrgId]. orgId:{}", orgId);

        //check orgId
        orgManager.verifyOrgId(orgId);

        //check orgId used
        if (hostManager.countByOrgId(orgId) > 0) {
            log.warn("org has bean used by host,orgId:{}", orgId);
            throw new WebServerException(ConstantCode.ORG_ID_HAS_BEEN_USED);
        }

        orgMapper.deleteByPrimaryKey(orgId);
        log.info("start exec method [deleteByOrgId]. orgId:{}", orgId);
    }

}
